//
// Created by zntx0 on 2025/9/28.
//
/*
  Copyright (c) 2009-2017 Dave Gamble and zJSON contributors

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

/* zJSON */
/* JSON parser in C. */

/* disable warnings about old C89 functions in MSVC */
#include <stdarg.h>

#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
#define _CRT_SECURE_NO_DEPRECATE
#endif

#ifdef __GNUC__
#pragma GCC visibility push(default)
#endif

#if defined(_MSC_VER)
#pragma warning (push)
/* disable warning about single line comments in system headers */
#pragma warning (disable : 4001)
#endif

#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <float.h>
#include <stdint.h>
#include <stdbool.h>
#include "zJSON.h"

#ifdef ENABLE_LOCALES
#include <locale.h>
#endif

#if defined(_MSC_VER)
#pragma warning (pop)
#endif

#ifdef __GNUC__
#pragma GCC visibility pop
#endif


/* define our own boolean type */

/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
#ifndef isinf
#define isinf(d) (isnan((d - d)) && !isnan(d))
#endif

#ifndef isnan
#define isnan(d) (d != d)
#endif

#ifndef NAN
    #ifdef _WIN32
        #define NAN sqrt(-1.0)
    #else
        #define NAN 0.0/0.0
    #endif
#endif

#if defined(_MSC_VER)
    /* work around MSVC error C2322: '...' address of dllimport '...' is not static */
    static void * zJSON_CDECL internal_malloc(size_t size)
    {
        return malloc(size);
    }
static void zJSON_CDECL internal_free(void *pointer)
    {
        free(pointer);
    }
static void * zJSON_CDECL internal_realloc(void *pointer, size_t size)
    {
        return realloc(pointer, size);
    }
#else
    #define internal_malloc malloc
    #define internal_free free
    #define internal_realloc realloc
#endif

/* check if the given size is left to read in a given parse buffer (starting with 1) */
#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
/* check if the buffer can be accessed at the given index (starting with 0) */
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
/* get a pointer to the buffer at the position */
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)

/* strlen of character literals resolved at compile time */
#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))

/* Parser core - when encountering text, process appropriately. */
static zJSON parse_value(zJSON * const item, int* depth);

typedef struct {
    const unsigned char *json;
    size_t position;
} error;


static error global_error = { NULL, 0 };

zJSON_PUBLIC(const char *) zJSON_GetErrorPtr(void)
{
    return (const char*) (global_error.json + global_error.position);
}


zJSON_PUBLIC(const char*) zJSON_Version(void)
{
    static char version[15];
    sprintf(version, "%i.%i.%i", zJSON_VERSION_MAJOR, zJSON_VERSION_MINOR, zJSON_VERSION_PATCH);

    return version;
}

/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
{
    if ((string1 == NULL) || (string2 == NULL))
    {
        return 1;
    }

    if (string1 == string2)
    {
        return 0;
    }

    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
    {
        if (*string1 == '\0')
        {
            return 0;
        }
    }

    return tolower(*string1) - tolower(*string2);
}


/* get the decimal point character of the current locale */
unsigned char get_decimal_point(void)
{
#ifdef ENABLE_LOCALES
    struct lconv *lconv = localeconv();
    return (unsigned char) lconv->decimal_point[0];
#else
    return '.';
#endif
}

/* Parse the input text to generate a number, and populate the result into item. */
static zJSON parse_number(zJSON * const item,  int* valueint, double* valuedouble)
{
    double number = 0;
    unsigned char *after_end = NULL;
    unsigned char number_c_string[64];
    unsigned char decimal_point = get_decimal_point();
    size_t i = 0;

    if (item->content == NULL)
    {
        return (zJSON){.type = zJSON_Invalid};
    }

    /* copy the number into a temporary buffer and replace '.' with the decimal point
     * of the current locale (for strtod)
     * This also takes care of '\0' not necessarily being available for marking the end of the input */
    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(item, i); i++)
    {
        switch (buffer_at_offset(item)[i])
        {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '+':
            case '-':
            case 'e':
            case 'E':
                number_c_string[i] = buffer_at_offset(item)[i];
                break;

            case '.':
                number_c_string[i] = decimal_point;
                break;

            default:
                goto loop_end;
        }
    }
loop_end:
    number_c_string[i] = '\0';

    number = strtod((const char*)number_c_string, (char**)&after_end);
    if (number_c_string == after_end)
    {
        return (zJSON){.type = zJSON_Invalid}; /* parse_error */
    }

    if (valuedouble)
        *valuedouble = number;

    /* use saturation in case of overflow */
    if (number >= INT_MAX)
    {
        if(valueint)
            *valueint = INT_MAX;
    }
    else if (number <= (double)INT_MIN)
    {
        if(valueint)
            *valueint = INT_MIN;
    }
    else
    {
        if(valueint)
            *valueint = (int)number;
    }

    zJSON json= {.content = buffer_at_offset(item), .length = (size_t)(after_end - number_c_string), .type = zJSON_Number};
    //item->type = zJSON_Number;
    item->offset += (size_t)(after_end - number_c_string);
    return json;
}


/* parse 4 digit hexadecimal number */
static unsigned parse_hex4(const unsigned char * const input)
{
    unsigned int h = 0;
    size_t i = 0;

    for (i = 0; i < 4; i++)
    {
        /* parse digit */
        if ((input[i] >= '0') && (input[i] <= '9'))
        {
            h += (unsigned int) input[i] - '0';
        }
        else if ((input[i] >= 'A') && (input[i] <= 'F'))
        {
            h += (unsigned int) 10 + input[i] - 'A';
        }
        else if ((input[i] >= 'a') && (input[i] <= 'f'))
        {
            h += (unsigned int) 10 + input[i] - 'a';
        }
        else /* invalid */
        {
            return 0;
        }

        if (i < 3)
        {
            /* shift left to make place for the next nibble */
            h = h << 4;
        }
    }

    return h;
}

/* converts a UTF-16 literal to UTF-8
 * A literal can be one or two sequences of the form \uXXXX */
static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer, bool compare)
{
    long unsigned int codepoint = 0;
    unsigned int first_code = 0;
    const unsigned char *first_sequence = input_pointer;
    unsigned char utf8_length = 0;
    unsigned char utf8_position = 0;
    unsigned char sequence_length = 0;
    unsigned char first_byte_mark = 0;

    if ((input_end - first_sequence) < 6)
    {
        /* input ends unexpectedly */
        goto fail;
    }

    /* get the first utf16 sequence */
    first_code = parse_hex4(first_sequence + 2);

    /* check that the code is valid */
    if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
    {
        goto fail;
    }

    /* UTF16 surrogate pair */
    if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
    {
        const unsigned char *second_sequence = first_sequence + 6;
        unsigned int second_code = 0;
        sequence_length = 12; /* \uXXXX\uXXXX */

        if ((input_end - second_sequence) < 6)
        {
            /* input ends unexpectedly */
            goto fail;
        }

        if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
        {
            /* missing second half of the surrogate pair */
            goto fail;
        }

        /* get the second utf16 sequence */
        second_code = parse_hex4(second_sequence + 2);
        /* check that the code is valid */
        if ((second_code < 0xDC00) || (second_code > 0xDFFF))
        {
            /* invalid second half of the surrogate pair */
            goto fail;
        }


        /* calculate the unicode codepoint from the surrogate pair */
        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
    }
    else
    {
        sequence_length = 6; /* \uXXXX */
        codepoint = first_code;
    }

    /* encode as UTF-8
     * takes at maximum 4 bytes to encode:
     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
    if (codepoint < 0x80)
    {
        /* normal ascii, encoding 0xxxxxxx */
        utf8_length = 1;
    }
    else if (codepoint < 0x800)
    {
        /* two bytes, encoding 110xxxxx 10xxxxxx */
        utf8_length = 2;
        first_byte_mark = 0xC0; /* 11000000 */
    }
    else if (codepoint < 0x10000)
    {
        /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
        utf8_length = 3;
        first_byte_mark = 0xE0; /* 11100000 */
    }
    else if (codepoint <= 0x10FFFF)
    {
        /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
        utf8_length = 4;
        first_byte_mark = 0xF0; /* 11110000 */
    }
    else
    {
        /* invalid unicode codepoint */
        goto fail;
    }

    /* encode as utf8 */
    for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
    {
        if (compare == false)
        {
            /* 10xxxxxx */
            (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
            codepoint >>= 6;
        }
        else
        {
            if ((*output_pointer)[utf8_position] != (unsigned char)((codepoint | 0x80) & 0xBF))
            {
                return 0;
            }
            codepoint >>= 6;
        }
    }
    /* encode first byte */
    if (utf8_length > 1)
    {
        if (compare == false)
        {
            (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
        }
        else if ((*output_pointer)[0] != (unsigned char)((codepoint | first_byte_mark) & 0xFF))
        {
            return 0;
        }
    }
    else
    {
        if (compare == false)
        {
            (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
        }
        else if ((*output_pointer)[0] != (unsigned char)(codepoint & 0x7F))
        {
            return 0;
        }
    }

    *output_pointer += utf8_length;

    return sequence_length;

fail:
    return 0;
}

/* Parse the input text into an unescaped cinput, and populate item. */
static zJSON parse_string(zJSON * const item, char *output, size_t output_size)
{
    const char *input_pointer = buffer_at_offset(item) + 1;
    const char *input_end = buffer_at_offset(item) + 1;
    char *output_pointer = NULL;
    //char *output = NULL;
    size_t allocation_length = 0;

    /* not a string */
    if (buffer_at_offset(item)[0] != '\"')
    {
        goto fail;
    }

    {
        /* calculate approximate size of the output (overestimate) */
        size_t skipped_bytes = 0;
        while (((size_t)(input_end - item->content) < item->length) && (*input_end != '\"'))
        {
            /* is escape sequence */
            if (input_end[0] == '\\')
            {
                if ((size_t)(input_end + 1 - item->content) >= item->length)
                {
                    /* prevent buffer overflow when last input character is a backslash */
                    goto fail;
                }
                skipped_bytes++;
                input_end++;
            }
            input_end++;
        }
        if (((size_t)(input_end - item->content) >= item->length) || (*input_end != '\"'))
        {
            goto fail; /* string ended unexpectedly */
        }

        /* This is at most how much we need for the output */
        allocation_length = (size_t) (input_end - buffer_at_offset(item)) - skipped_bytes;
        // output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
        // if (output == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        if ( output != NULL )
        {
            if (output_size < allocation_length)
                goto fail;
        }
    }

    if (output != NULL)
    {
        output_pointer = output;
        /* loop through the string literal */
        while (input_pointer < input_end)
        {
            if (*input_pointer != '\\')
            {
                *output_pointer++ = *input_pointer++;
            }
            /* escape sequence */
            else
            {
                unsigned char sequence_length = 2;
                if ((input_end - input_pointer) < 1)
                {
                    goto fail;
                }

                switch (input_pointer[1])
                {
                case 'b':
                    *output_pointer++ = '\b';
                    break;
                case 'f':
                    *output_pointer++ = '\f';
                    break;
                case 'n':
                    *output_pointer++ = '\n';
                    break;
                case 'r':
                    *output_pointer++ = '\r';
                    break;
                case 't':
                    *output_pointer++ = '\t';
                    break;
                case '\"':
                case '\\':
                case '/':
                    *output_pointer++ = input_pointer[1];
                    break;

                    /* UTF-16 literal */
                case 'u':
                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer, false);
                    if (sequence_length == 0)
                    {
                        /* failed to convert UTF16-literal to UTF-8 */
                        goto fail;
                    }
                    break;

                default:
                    goto fail;
                }
                input_pointer += sequence_length;
            }
        }

        /* zero terminate the output */
        *output_pointer = '\0';
    }
    zJSON json= {.content = buffer_at_offset(item),.length = (size_t) (input_end - buffer_at_offset(item)) + 1, .type = zJSON_String};

    //item->type = zJSON_String;
    item->offset = (size_t) (input_end - item->content);
    item->offset++;

    return json;

fail:

    if (input_pointer != NULL)
    {
        item->offset = (size_t)(input_pointer - item->content);
    }

    return (zJSON){.type = zJSON_Invalid};;
}

/* Utility to jump whitespace and cr/lf */
static zJSON *buffer_skip_whitespace(zJSON * const buffer)
{
    if ((buffer == NULL) || (buffer->content == NULL))
    {
        return NULL;
    }

    if (cannot_access_at_index(buffer, 0))
    {
        return buffer;
    }

    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
    {
       buffer->offset++;
    }

    if (buffer->offset == buffer->length)
    {
        buffer->offset--;
    }

    return buffer;
}

/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
static zJSON *skip_utf8_bom(zJSON * const buffer)
{
    if ((buffer->content == NULL) || (buffer->offset != 0))
    {
        return NULL;
    }

    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
    {
        buffer->offset += 3;
    }

    return buffer;
}



#define zJSON_min(a, b) (((a) < (b)) ? (a) : (b))


/* Build an array from input text. */
static zJSON parse_array(zJSON * const item, int* depth)
{
    //zJSON *head = NULL; /* head of the linked list */
    //zJSON *current_item = NULL;

    if (*depth >= zJSON_NESTING_LIMIT)
    {
        return (zJSON){.type = zJSON_Invalid}; /* to deeply nested */
    }
    *depth++;

    size_t offset = item->offset;

    if (buffer_at_offset(item)[0] != '[')
    {
        /* not an array */
        goto fail;
    }

    item->offset++;
    buffer_skip_whitespace(item);
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ']'))
    {
        /* empty array */
        goto success;
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(item, 0))
    {
        item->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    item->offset--;
    /* loop through the comma separated array elements */
    do
    {
        /* allocate next item */
        // zJSON *new_item = zJSON_New_Item(&(input_buffer->hooks));
        // if (new_item == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }

        /* attach next item to list */
        // if (head == NULL)
        // {
        //     /* start the linked list */
        //     current_item = head = new_item;
        // }
        // else
        // {
        //     /* add to the end and advance */
        //     current_item->next = new_item;
        //     new_item->prev = current_item;
        //     current_item = new_item;
        // }

        /* parse next value */
        item->offset++;
        buffer_skip_whitespace(item);
        if (parse_value(item, depth).type == zJSON_Invalid)
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(item);
    }
    while (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ','));

    if (cannot_access_at_index(item, 0) || buffer_at_offset(item)[0] != ']')
    {
        goto fail; /* expected end of array */
    }

success:
    //item->depth--;

    // if (head != NULL) {
    //     head->prev = current_item;
    // }
    zJSON json = {.type = zJSON_Array, .content = item->content +  offset, .length = item->offset - offset + 1};
    item->type = zJSON_Array;
    //item->child = head;

    item->offset++;

    return json;

fail:
    // if (head != NULL)
    // {
    //     zJSON_Delete(head);
    // }

    return (zJSON){.type = zJSON_Invalid};
}


/* Build an object from the text. */
static zJSON parse_object(zJSON * const item, int* depth)
{
    //zJSON *head = NULL; /* linked list head */
    //zJSON *current_item = NULL;

    if (*depth >= zJSON_NESTING_LIMIT)
    {
        return (zJSON){.type = zJSON_Invalid}; /* to deeply nested */
    }
    *depth++;

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '{'))
    {
        goto fail; /* not an object */
    }

    size_t offset = item->offset;
    item->offset++;
    buffer_skip_whitespace(item);
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '}'))
    {
        goto success; /* empty object */
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(item, 0))
    {
        item->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    item->offset--;
    /* loop through the comma separated array elements */
    do
    {
        /* allocate next item */
        // zJSON *new_item = zJSON_New_Item(&(input_buffer->hooks));
        // if (new_item == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        //
        // /* attach next item to list */
        // if (head == NULL)
        // {
        //     /* start the linked list */
        //     current_item = head = new_item;
        // }
        // else
        // {
        //     /* add to the end and advance */
        //     current_item->next = new_item;
        //     new_item->prev = current_item;
        //     current_item = new_item;
        // }

        if (cannot_access_at_index(item, 1))
        {
            goto fail; /* nothing comes after the comma */
        }

        /* parse the name of the child */
        item->offset++;
        buffer_skip_whitespace(item);
        if (parse_string(item, NULL, 0).type != zJSON_String)
        {
            goto fail; /* failed to parse name */
        }
        buffer_skip_whitespace(item);

        /* swap valuestring and string, because we parsed the name */
        //current_item->string = current_item->valuestring;
        //current_item->valuestring = NULL;

        if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != ':'))
        {
            goto fail; /* invalid object */
        }

        /* parse the value */
        item->offset++;
        buffer_skip_whitespace(item);
        if (parse_value(item, depth).type == zJSON_Invalid)
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(item);
    }
    while (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ','));

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '}'))
    {
        goto fail; /* expected end of object */
    }

success:
    // item->depth--;
    //
    // if (head != NULL) {
    //     head->prev = current_item;
    // }
    zJSON json= {.content = item->content + offset,.length = item->offset - offset, .type = zJSON_Object};
    //item->type = zJSON_Object;
    //item->child = head;
    item->offset++;
    return json;

fail:
    // if (head != NULL)
    // {
    //     zJSON_Delete(head);
    // }

    return (zJSON){.type = zJSON_Invalid};
}



/* Parser core - when encountering text, process appropriately. */
static zJSON parse_value(zJSON * const item, int* depth)
{
    if (item == NULL || (item->content == NULL))
    {
        return (zJSON){.type = zJSON_Invalid}; /* no input */
    }

    /* parse the different types of values */
    /* null */
    if (can_read(item, 4) && (strncmp((const char*)buffer_at_offset(item), "null", 4) == 0))
    {
        //item->type = zJSON_NULL;
        zJSON json = {.content = buffer_at_offset(item),.length = 4, .type = zJSON_NULL};
        item->offset += 4;
        return json;
    }
    /* false */
    if (can_read(item, 5) && (strncmp((const char*)buffer_at_offset(item), "false", 5) == 0))
    {
        zJSON json = {.content = buffer_at_offset(item),.length = 5, .type = zJSON_Bool};
        //item->type = zJSON_False;
        item->offset += 5;
        return json;
    }
    /* true */
    if (can_read(item, 4) && (strncmp((const char*)buffer_at_offset(item), "true", 4) == 0))
    {
        zJSON json = {.content = buffer_at_offset(item),.length = 4, .type = zJSON_Bool};
        //item->type = zJSON_True;
        item->offset += 4;
        return json;
    }
    /* string */
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '\"'))
    {
        return parse_string(item, NULL, 0);
    }
    /* number */
    if (can_access_at_index(item, 0) && ((buffer_at_offset(item)[0] == '-') || ((buffer_at_offset(item)[0] >= '0') && (buffer_at_offset(item)[0] <= '9'))))
    {
        return parse_number(item, NULL, NULL);
    }
    /* array */
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '['))
    {
        return parse_array(item, depth);
    }
    /* object */
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '{'))
    {
        return parse_object(item, depth);
    }

    return (zJSON){.type = zJSON_Invalid};
}

/* Parse an object - create a new root, and populate. */
zJSON_PUBLIC(zJSON) zJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, bool require_null_terminated)
{
    if (value == NULL || 0 == buffer_length) {
        goto fail;
    }

    zJSON buffer = {
        .type = zJSON_Raw,
        .content = value,
        .length = buffer_length,
        .offset = 0,
        //buffer.hooks = global_hooks;
    };

    zJSON* item = buffer_skip_whitespace(skip_utf8_bom(&buffer));
    size_t offset = item->offset;
    int depth = 0;

    /* parse the different types of values */
    /* null */
    if (can_read(item, 4) && (strncmp((const char*)buffer_at_offset(item), "null", 4) == 0))
    {
        item->type = zJSON_NULL;
        item->offset += 4;
        item->length = item->offset - offset;
        item->content = item->content + offset;
    }
    /* false */
    else if (can_read(item, 5) && (strncmp((const char*)buffer_at_offset(item), "false", 5) == 0))
    {
        item->type = zJSON_Bool;
        item->offset += 5;
        item->length = item->offset - offset;
        item->content = item->content + offset;
    }
    /* true */
    else if (can_read(item, 4) && (strncmp((const char*)buffer_at_offset(item), "true", 4) == 0))
    {
        item->type = zJSON_Bool;
        //item->valueint = 1;
        item->offset += 4;
        item->length = item->offset - offset;
        item->content = item->content + offset;
    }
    /* string */
    else if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '\"'))
    {
        zJSON json = parse_string(item, NULL, 0);
        item->type = zJSON_String;
        item->length = json.offset - offset;
        item->content = item->content + offset;
    }
    /* number */
    else if (can_access_at_index(item, 0) && ((buffer_at_offset(item)[0] == '-') || ((buffer_at_offset(item)[0] >= '0') && (buffer_at_offset(item)[0] <= '9'))))
    {
        zJSON json = parse_number(item, NULL, NULL);
        item->type = zJSON_Number;
        item->length = json.offset - offset;
        item->content = item->content + offset;
    }
    /* array */
    else if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '['))
    {
        zJSON json = parse_array(item, &depth);
        item->type = zJSON_Array;
        item->length = json.offset - offset;
        item->content = item->content + offset;
    }
    /* object */
    else if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '{'))
    {
        zJSON json = parse_object(item, &depth);
        item->type = zJSON_Object;
        item->length = json.offset - offset;
        item->content = item->content + offset;
    }
    //zJSON json = parse_value(buffer_skip_whitespace(skip_utf8_bom(&item)));
    else
    {
        /* parse failure. ep is set. */
        goto fail;
    }

    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
    if (require_null_terminated)
    {
        buffer_skip_whitespace(item);
        if ((item->offset >= item->length) || buffer_at_offset(item)[0] != '\0')
        {
            goto fail;
        }
    }
    if (return_parse_end)
    {
        *return_parse_end = (const char*)buffer_at_offset(item);
    }

    return buffer;

    fail:
        if (value != NULL)
        {
            error local_error;
            local_error.json = (const unsigned char*)value;
            local_error.position = 0;

            if (item->offset < item->length)
            {
                local_error.position = item->offset;
            }
            else if (item->length > 0)
            {
                local_error.position = item->length - 1;
            }

            if (return_parse_end != NULL)
            {
                *return_parse_end = (const char*)local_error.json + local_error.position;
            }

            global_error = local_error;
        }

    return (zJSON){.type = zJSON_Invalid};;
}

zJSON_PUBLIC(zJSON) zJSON_ParseWithOpts(const char *value, const char **return_parse_end, bool require_null_terminated)
{
    if (NULL == value)
    {
        return (zJSON){.type = zJSON_Invalid};
    }

    /* Adding null character size due to require_null_terminated. */
    size_t buffer_length = strlen(value) + sizeof("");

    return zJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
}

/* Default options for zJSON_Parse */
zJSON_PUBLIC(zJSON) zJSON_Parse(const char *value)
{
    return zJSON_ParseWithOpts(value, 0, 0);
}

zJSON_PUBLIC(zJSON) zJSON_ParseWithLength(const char *value, size_t buffer_length)
{
    return zJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
}

/***********************************************************
 *
 */

static zJSON zJSON_Duplicate(const zJSON* const json)
{
    if (json == NULL)
        return (zJSON){.type = zJSON_Invalid};

    return (zJSON){.type = json->type, .content = json->content + json->offset, .length = json->length - json->offset};
}

/* Parse the input text into an unescaped cinput, and populate item. */
static bool compare_string(const zJSON * const item, const char *output, size_t output_size, const bool case_sensitive)
{
    const char *input_pointer = buffer_at_offset(item) + 1;
    const char *input_end = buffer_at_offset(item) + 1;
    char *output_pointer = NULL;
    //char *output = NULL;
    //size_t allocation_length = 0;

    /* not a string */
    if (buffer_at_offset(item)[0] != '\"')
    {
        printf("%s:%d >>>> string ",  strrchr(__FILE__, '/'), __LINE__);
        goto fail;
    }

    {
        /* calculate approximate size of the output (overestimate) */
        size_t skipped_bytes = 0;
        while (((size_t)(input_end - item->content) < item->length) && (*input_end != '\"'))
        {
            /* is escape sequence */
            if (input_end[0] == '\\')
            {
                if ((size_t)(input_end + 1 - item->content) >= item->length)
                {
                    /* prevent buffer overflow when last input character is a backslash */
                    printf("%s:%d >>>> string ",  strrchr(__FILE__, '/'), __LINE__);
                    goto fail;
                }
                skipped_bytes++;
                input_end++;
            }
            input_end++;
        }
        if (((size_t)(input_end - item->content) >= item->length) || (*input_end != '\"'))
        {
            printf("%s:%d >>>> string ",  strrchr(__FILE__, '/'), __LINE__);
            goto fail; /* string ended unexpectedly */
        }

        /* This is at most how much we need for the output */
        size_t allocation_length = (size_t) (input_end - buffer_at_offset(item)) - skipped_bytes  - 1;
        // output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
        // if (output == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        if ( output != NULL )
        {
            if (output_size < allocation_length)
            {
                goto fail;
            }
        }
    }

    output_pointer = output;
    /* loop through the string literal */
    while (input_pointer < input_end)
    {
        if (*input_pointer != '\\')
        {
            if ( case_sensitive)
            {
                if (tolower(*output_pointer) != tolower(*input_pointer))
                {
                    goto fail;
                }
                output_pointer++;
                input_pointer++;
            }
            else
            {
                if (*output_pointer++ != *input_pointer++)
                {
                    goto fail;
                }
            }
        }
        /* escape sequence */
        else
        {
            unsigned char sequence_length = 2;
            if ((input_end - input_pointer) < 1)
            {
                printf("%s:%d >>>> string ",  strrchr(__FILE__, '/'), __LINE__);
                goto fail;
            }

            switch (input_pointer[1])
            {
            case 'b':
                if (*output_pointer++ != '\b')
                {
                    goto fail;
                }
                break;
            case 'f':
                if (*output_pointer++ != '\f')
                {
                    goto fail;
                }
                break;
            case 'n':
                if (*output_pointer++ != '\n')
                {
                    goto fail;
                }
                break;
            case 'r':
                if (*output_pointer++ != '\r')
                {
                    goto fail;
                }
                break;
            case 't':
                if (*output_pointer++ != '\t')
                {
                    goto fail;
                }
                break;
            case '\"':
            case '\\':
            case '/':
                if (*output_pointer++ != input_pointer[1])
                {
                    goto fail;
                }
                break;

                /* UTF-16 literal */
            case 'u':
                sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer, true);
                if (sequence_length == 0)
                {
                    /* failed to convert UTF16-literal to UTF-8 */
                    goto fail;
                }
                break;

            default:
                goto fail;
            }
            input_pointer += sequence_length;
        }
    }

    /* zero terminate the output */
    //*output_pointer = '\0';

    return true;

fail:

    return false;
}


/* Build an object from the text. */
zJSON_PUBLIC(zJSON) zJSON_GetObjectItem(const zJSON* const object, const char* const string)
{
    zJSON value = {.type = zJSON_Invalid};
    zJSON item_object = zJSON_Duplicate(object);
    zJSON* item = &item_object;
    int depth = 0;

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '{'))
    {
        goto fail; /* not an object */
    }

    item->offset++;
    buffer_skip_whitespace(item);
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '}'))
    {
        goto success; /* empty object */
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(item, 0))
    {
        item->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    item->offset--;
    /* loop through the comma separated array elements */
    do
    {
        /* allocate next item */
        // zJSON *new_item = zJSON_New_Item(&(input_buffer->hooks));
        // if (new_item == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        //
        // /* attach next item to list */
        // if (head == NULL)
        // {
        //     /* start the linked list */
        //     current_item = head = new_item;
        // }
        // else
        // {
        //     /* add to the end and advance */
        //     current_item->next = new_item;
        //     new_item->prev = current_item;
        //     current_item = new_item;
        // }

        if (cannot_access_at_index(item, 1))
        {
            goto fail; /* nothing comes after the comma */
        }

        /* parse the name of the child */
        item->offset++;
        buffer_skip_whitespace(item);
        zJSON key = parse_string(item, NULL, 0);
        if (key.type != zJSON_String)
        {
            goto fail; /* failed to parse name */
        }

        buffer_skip_whitespace(item);

        /* swap valuestring and string, because we parsed the name */
        //current_item->string = current_item->valuestring;
        //current_item->valuestring = NULL;

        if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != ':'))
        {
            goto fail; /* invalid object */
        }

        /* parse the value */
        item->offset++;
        buffer_skip_whitespace(item);
        value = parse_value(item, &depth);
        if (value.type == zJSON_Invalid)
        {
            goto fail; /* failed to parse value */
        }

        // printf("%s:%d >>>> string %s ", __FILE__, __LINE__, string);
        // zJSON_Print(&key);
        if ( compare_string(&key, string, strlen(string), false))
            goto success; /* empty object */

        buffer_skip_whitespace(item);
    }
    while (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ','));

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '}'))
    {
        goto fail; /* expected end of object */
    }

success:
    // item->depth--;
    //
    // if (head != NULL) {
    //     head->prev = current_item;
    // }
    //codezJSON json= {.content = item->content + offset,.length = item->offset - offset, .type = zJSON_Object};
    //item->type = zJSON_Object;
    //item->child = head;
    item->offset++;
    return value;

fail:
    // if (head != NULL)
    // {
    //     zJSON_Delete(head);
    // }

    return (zJSON){.type = zJSON_Invalid};
}

zJSON_PUBLIC(zJSON) zJSON_GetObjectItemCaseSensitive(const zJSON* const object, const char* const string)
{

    zJSON value = {.type = zJSON_Invalid};
    zJSON item_object = zJSON_Duplicate(object);
    zJSON* item = &item_object;
    int depth = 0;

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '{'))
    {
        goto fail; /* not an object */
    }

    item->offset++;
    buffer_skip_whitespace(item);
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '}'))
    {
        goto success; /* empty object */
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(item, 0))
    {
        item->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    item->offset--;
    /* loop through the comma separated array elements */
    do
    {
        /* allocate next item */
        // zJSON *new_item = zJSON_New_Item(&(input_buffer->hooks));
        // if (new_item == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        //
        // /* attach next item to list */
        // if (head == NULL)
        // {
        //     /* start the linked list */
        //     current_item = head = new_item;
        // }
        // else
        // {
        //     /* add to the end and advance */
        //     current_item->next = new_item;
        //     new_item->prev = current_item;
        //     current_item = new_item;
        // }

        if (cannot_access_at_index(item, 1))
        {
            goto fail; /* nothing comes after the comma */
        }

        /* parse the name of the child */
        item->offset++;
        buffer_skip_whitespace(item);
        zJSON key = parse_string(item, NULL, 0);
        if (key.type != zJSON_String)
        {
            goto fail; /* failed to parse name */
        }

        buffer_skip_whitespace(item);

        /* swap valuestring and string, because we parsed the name */
        //current_item->string = current_item->valuestring;
        //current_item->valuestring = NULL;

        if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != ':'))
        {
            goto fail; /* invalid object */
        }

        /* parse the value */
        item->offset++;
        buffer_skip_whitespace(item);
        value = parse_value(item, &depth);
        if (value.type == zJSON_Invalid)
        {
            goto fail; /* failed to parse value */
        }

        // printf("%s:%d >>>> string %s ", __FILE__, __LINE__, string);
        // zJSON_Print(&key);
        if ( compare_string(&key, string, strlen(string), true))
            goto success; /* empty object */

        buffer_skip_whitespace(item);
    }
    while (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ','));

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '}'))
    {
        goto fail; /* expected end of object */
    }

success:
    // item->depth--;
    //
    // if (head != NULL) {
    //     head->prev = current_item;
    // }
    //codezJSON json= {.content = item->content + offset,.length = item->offset - offset, .type = zJSON_Object};
    //item->type = zJSON_Object;
    //item->child = head;
    item->offset++;
    return value;

fail:
    // if (head != NULL)
    // {
    //     zJSON_Delete(head);
    // }

    return (zJSON){.type = zJSON_Invalid};
}

zJSON_PUBLIC(bool) cJSON_HasObjectItem(const zJSON *object, const char *string)
{
    zJSON value = {.type = zJSON_Invalid};
    zJSON item_object = zJSON_Duplicate(object);
    zJSON* item = &item_object;
    int depth = 0;

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '{'))
    {
        goto fail; /* not an object */
    }

    item->offset++;
    buffer_skip_whitespace(item);
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == '}'))
    {
        goto success; /* empty object */
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(item, 0))
    {
        item->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    item->offset--;
    /* loop through the comma separated array elements */
    do
    {
        /* allocate next item */
        // zJSON *new_item = zJSON_New_Item(&(input_buffer->hooks));
        // if (new_item == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        //
        // /* attach next item to list */
        // if (head == NULL)
        // {
        //     /* start the linked list */
        //     current_item = head = new_item;
        // }
        // else
        // {
        //     /* add to the end and advance */
        //     current_item->next = new_item;
        //     new_item->prev = current_item;
        //     current_item = new_item;
        // }

        if (cannot_access_at_index(item, 1))
        {
            goto fail; /* nothing comes after the comma */
        }

        /* parse the name of the child */
        item->offset++;
        buffer_skip_whitespace(item);
        zJSON key = parse_string(item, NULL, 0);
        if (key.type != zJSON_String)
        {
            goto fail; /* failed to parse name */
        }

        if ( compare_string(&key, string, strlen(string), true))
            goto success; /* empty object */

        buffer_skip_whitespace(item);

        /* swap valuestring and string, because we parsed the name */
        //current_item->string = current_item->valuestring;
        //current_item->valuestring = NULL;

        if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != ':'))
        {
            goto fail; /* invalid object */
        }

        /* parse the value */
        item->offset++;
        buffer_skip_whitespace(item);
        value = parse_value(item, &depth);
        if (value.type == zJSON_Invalid)
        {
            goto fail; /* failed to parse value */
        }

        // printf("%s:%d >>>> string %s ", __FILE__, __LINE__, string);
        // zJSON_Print(&key);
        buffer_skip_whitespace(item);
    }
    while (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ','));

    if (cannot_access_at_index(item, 0) || (buffer_at_offset(item)[0] != '}'))
    {
        goto fail; /* expected end of object */
    }

success:
    // item->depth--;
    //
    // if (head != NULL) {
    //     head->prev = current_item;
    // }
    //codezJSON json= {.content = item->content + offset,.length = item->offset - offset, .type = zJSON_Object};
    //item->type = zJSON_Object;
    //item->child = head;
    item->offset++;
    return true;

fail:
    // if (head != NULL)
    // {
    //     zJSON_Delete(head);
    // }

    return false;
}

/* Build an array from input text. */
zJSON_PUBLIC(int) zJSON_GetArraySize(const zJSON* array)
{
    // cJSON *head = NULL; /* head of the linked list */
    // cJSON *current_item = NULL;
    //
    // if (input_buffer->depth >= CJSON_NESTING_LIMIT)
    // {
    //     return false; /* to deeply nested */
    // }
    // input_buffer->depth++;
    zJSON item_object = zJSON_Duplicate(array);
    zJSON* item = &item_object;
    int number = 0;
    int depth = 0;

    if (buffer_at_offset(item)[0] != '[')
    {
        /* not an array */
        goto fail;
    }

    item->offset++;
    buffer_skip_whitespace(item);
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ']'))
    {
        /* empty array */
        goto success;
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(item, 0))
    {
        item->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    item->offset--;
    /* loop through the comma separated array elements */
    do
    {
        // /* allocate next item */
        // cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
        // if (new_item == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        //
        // /* attach next item to list */
        // if (head == NULL)
        // {
        //     /* start the linked list */
        //     current_item = head = new_item;
        // }
        // else
        // {
        //     /* add to the end and advance */
        //     current_item->next = new_item;
        //     new_item->prev = current_item;
        //     current_item = new_item;
        // }

        /* parse next value */
        item->offset++;
        buffer_skip_whitespace(item);
        if (parse_value(item, &depth).type == zJSON_Invalid)
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(item);
        number++;
    }
    while (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ','));

    if (cannot_access_at_index(item, 0) || buffer_at_offset(item)[0] != ']')
    {
        goto fail; /* expected end of array */
    }

success:
    //item->depth--;

    // if (head != NULL) {
    //     head->prev = current_item;
    // }

    item->type = zJSON_Array;
    //item->child = head;

    item->offset++;

    return number;

fail:
    // if (head != NULL)
    // {
    //     cJSON_Delete(head);
    // }

    return 0;
}

/* Build an array from input text. */
zJSON_PUBLIC(zJSON) zJSON_GetArrayItem(const zJSON* array, int index)
{
    // cJSON *head = NULL; /* head of the linked list */
    // cJSON *current_item = NULL;
    //
    // if (input_buffer->depth >= CJSON_NESTING_LIMIT)
    // {
    //     return false; /* to deeply nested */
    // }
    // input_buffer->depth++;
    zJSON value = {.type = zJSON_Invalid};
    zJSON item_object = zJSON_Duplicate(array);
    zJSON* item = &item_object;
    int number = 0;
    int depth = 0;

    if (buffer_at_offset(item)[0] != '[')
    {
        /* not an array */
        goto fail;
    }

    item->offset++;
    buffer_skip_whitespace(item);
    if (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ']'))
    {
        /* empty array */
        goto success;
    }

    /* check if we skipped to the end of the buffer */
    if (cannot_access_at_index(item, 0))
    {
        item->offset--;
        goto fail;
    }

    /* step back to character in front of the first element */
    item->offset--;
    /* loop through the comma separated array elements */
    do
    {
        // /* allocate next item */
        // cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
        // if (new_item == NULL)
        // {
        //     goto fail; /* allocation failure */
        // }
        //
        // /* attach next item to list */
        // if (head == NULL)
        // {
        //     /* start the linked list */
        //     current_item = head = new_item;
        // }
        // else
        // {
        //     /* add to the end and advance */
        //     current_item->next = new_item;
        //     new_item->prev = current_item;
        //     current_item = new_item;
        // }

        /* parse next value */
        item->offset++;
        buffer_skip_whitespace(item);
        value = parse_value(item, &depth);
        if (value.type == zJSON_Invalid)
        {
            goto fail; /* failed to parse value */
        }
        buffer_skip_whitespace(item);
        if (number == index)
            goto success;
        number++;
    }
    while (can_access_at_index(item, 0) && (buffer_at_offset(item)[0] == ','));

    if (cannot_access_at_index(item, 0) || buffer_at_offset(item)[0] != ']')
    {
        goto fail; /* expected end of array */
    }

success:
    //item->depth--;

    // if (head != NULL) {
    //     head->prev = current_item;
    // }

    item->type = zJSON_Array;
    //item->child = head;

    item->offset++;

    return value;

fail:
    // if (head != NULL)
    // {
    //     cJSON_Delete(head);
    // }

    return (zJSON){.type = zJSON_Invalid};
}


zJSON_PUBLIC(bool) zJSON_GetBoolValue(const zJSON* const json)
{
    if (json == NULL || json->type != zJSON_Bool)
        return false;

    if (can_read(json, 4) && (strncmp((const char*)buffer_at_offset(json), "true", 4) == 0))
    {
        return true;
    }

    if (can_read(json, 4) && (strncmp((const char*)buffer_at_offset(json), "false", 4) == 0))
    {
        return false;
    }

    return false;
}

zJSON_PUBLIC(int) zJSON_GetIntValue(const zJSON* const json)
{
    if (json == NULL || json->type != zJSON_Number)
        return 0;

    int number = 0;
    zJSON item = zJSON_Duplicate(json);

    if (parse_number(&item, &number, NULL).type != zJSON_Number)
        return 0;

    return number;
}

zJSON_PUBLIC(double) zJSON_GetDoubleValue(const zJSON* const json)
{
    if (json == NULL || json->type != zJSON_Number)
        return 0.0;

    double number = 0;
    zJSON item = zJSON_Duplicate(json);

    if (parse_number(&item, NULL, &number).type != zJSON_Number)
        return 0.0;

    return number;
}

zJSON_PUBLIC(char*) zJSON_GetStringValue(const zJSON* const json, char* data, size_t size)
{
    if (json == NULL || json->type != zJSON_String || data == NULL || size == 0)
        return NULL;

    zJSON item = zJSON_Duplicate(json);
    parse_string(&item, data, size);
    return data;
}

zJSON_PUBLIC(size_t) zJSON_GetStringValueSize(const zJSON* const json)
{
    return  json == NULL ? 0 : json->length;
}

// 变参函数：接收多个字符，以'\0'作为结束标志
zJSON_PUBLIC(zJSON) zJSON_Get(const zJSON* json, ...)
{
    zJSON contest = zJSON_Duplicate(json);
    va_list args;
    va_start(args, NULL);  // 初始化变参列表

    // 循环获取字符串，直到遇到NULL结束标志
    while (1)
    {
        char *str = va_arg(args, char*);
        // 遇到结束标志则退出循环
        if (str == NULL)
        {
            break;
        }

        contest = zJSON_GetObjectItem(&contest , str);
        if ( contest.type == zJSON_Invalid )
        {
            printf("zJSON_Invalid - %s\n", str);
            break;
        }
    }

    va_end(args);  // 清理变参列表
    return contest;
}

